package com.weishao.dbmapper.engine;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.JdbcUtils;
import com.weishao.common.constant.Constants;
import com.weishao.common.util.DatabaseAwareUtils;
import com.weishao.dbmapper.IResultRowHandler;
import com.weishao.dbmapper.pojo.TaskParamEntity;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractMapperService implements IMapperService {

	protected JdbcTemplate jdbcTemplate;

	protected int fetchSize;

	protected boolean quiet;

	public AbstractMapperService(DataSource dataSource, int fetchSize, boolean quiet) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
		this.fetchSize = fetchSize;
		this.quiet = quiet;
	}

	@Override
	public abstract void mapper(TaskParamEntity task, IResultRowHandler handler);

	/**
	 * 多对一映射转换
	 * 
	 * @param querySql      多对一查询SQL语句
	 * @param handler       转换记录处理器
	 * @param targetColumns 转换后的目的字段列表
	 * @param orderMap      字段转换的对应关系( 源字段名->目的字段索引号集合 )
	 * @param valuesMapper  值转换关系 ( 目的字段名 -> map(源值key->目的值value) )
	 * @param fetchSize     批量查询的fetch-size大小
	 */
	protected final void doDataTransfer(String querySql, IResultRowHandler handler, List<String> targetColumns,
			Map<String, Set<Integer>> orderMap, Map<String, Map<String, String>> valuesMapper) {

		jdbcTemplate.execute(new ConnectionCallback<Boolean>() {

			public Boolean doInConnection(Connection con) throws SQLException, DataAccessException {
				Statement stmt = null;
				ResultSet rs = null;

				try {
					stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
					stmt.setQueryTimeout(Constants.DEFAULT_QUERY_TIMEOUT_SECONDS);
					if (DatabaseAwareUtils.isUsingMySQL(AbstractMapperService.this.jdbcTemplate.getDataSource())) {
						stmt.setFetchSize(Integer.MIN_VALUE);
					} else {
						stmt.setFetchSize(AbstractMapperService.this.fetchSize);
					}

					if (log.isDebugEnabled()) {
						log.debug("Execute Query SQL:{}", querySql);
					}

					long startTime = System.currentTimeMillis();

					rs = stmt.executeQuery(querySql);

					if (log.isDebugEnabled()) {
						float seconds = (System.currentTimeMillis() - startTime) / 1000F;
						log.info("Execute SQL elipse {} seconds, SQL: {}", seconds, querySql);
					}

					while (rs.next()) {
						ResultSetMetaData metaData = rs.getMetaData();
						Object args[] = new Object[targetColumns.size()];
						for (int j = 1; j <= metaData.getColumnCount(); ++j) {
							String skey = metaData.getColumnLabel(j);// source key field name
							if (null == skey) {
								skey = metaData.getColumnName(j);
							}

							if (!orderMap.containsKey(skey)) {
								continue;
							}

							Set<Integer> nrs = orderMap.get(skey);
							for (Integer k : nrs) {
								try {
									String tkey = targetColumns.get(k);// target key field name
									if (valuesMapper.containsKey(tkey)) {
										// 进行值转换
										String val = rs.getString(j);
										if (valuesMapper.get(tkey).containsKey(val)) {
											args[k] = valuesMapper.get(tkey).get(val);
										} else {
											if (AbstractMapperService.this.quiet) {
												args[k] = val;
											} else {
												throw new RuntimeException(
														String.format("字段%s=>%s映射的值%s不存在映射关系", skey, tkey, val));
											}
										}
									} else {
										// 无需值转换
										args[k] = rs.getObject(j);
									}
								} catch (SQLException e) {
									args[k] = null;
									log.warn("在处理查询到的行数据调用getObject() 错误:", e.getMessage());
								}
							} // for
						}

						handler.handle(Collections.unmodifiableList(targetColumns), args);
					} // while

					handler.destroy(Collections.unmodifiableList(targetColumns));
				} finally {
					JdbcUtils.closeResultSet(rs);
					JdbcUtils.closeStatement(stmt);
				}

				return true;
			}
		});

	}
}
